import {
  forwardRef,
  ChangeDetectorRef,
  Component,
  ElementRef,
  EventEmitter,
  HostListener,
  Input,
  OnDestroy,
  OnInit,
  Output,
  TemplateRef,
  ViewChild, Host, Optional
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

import { CdkConnectedOverlay, ConnectedOverlayPositionChange } from '@angular/cdk/overlay';


import { deepCompare, exists,  missingOrEmptyObject } from '../../../utils/utils';
import { AreaService } from '@core/services/masterdata/area.service';
import { map, mergeMap } from 'rxjs/operators';
import { ApesNoAnimationDirective, ApesUpdateHostClassService, slideMotion, toBoolean } from '@shared/apes/core';


const defaultDisplayRender = label => label.join(' / ');

export type ApesCascaderExpandTrigger = 'click' | 'hover';
export type ApesCascaderTriggerType = 'click' | 'hover';
export type ApesCascaderSize = 'small' | 'large' | 'default' ;

export interface CascaderOption {
  id?:any;
  name?:any;
  value?: any;
  label?: string;
  title?: string;
  disabled?: boolean;
  loading?: boolean;
  isLeaf?: boolean;
  parent?: CascaderOption;
  children?: CascaderOption[];

  [ key: string ]: any;
}

@Component({
  selector           : 'apes-city-picker,[apes-city-picker]',

  animations         : [
    slideMotion
  ],
  templateUrl        : './apes-city-picker.component.html',
  preserveWhitespaces: false,
  providers          : [
    ApesUpdateHostClassService,
    {
      provide    : NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ApesCityPickerComponent),
      multi      : true
    }
  ],
  host               : {
    '[attr.tabIndex]': '"0"'
  }
})
export class ApesCityPickerComponent implements OnInit, OnDestroy, ControlValueAccessor {
  /** 浮层菜单 */
  @ViewChild('input', { static: false }) input: ElementRef;
  @ViewChild('menu', { static: false }) menu: ElementRef;
  @ViewChild(CdkConnectedOverlay, { static: false }) overlay: CdkConnectedOverlay;

  private allowClear = true;
  private autoFocus = false;
  private disabled = false;
  private readonly = false;
  private showArrow = true;
  private showInput = true;
  private size: ApesCascaderSize = 'default';
  private menuClassName;
  private columnClassName;
  private changeOnSelect = false;
  // private showSearch = false;
  private defaultValue={};
  public dropDownPosition = 'bottom';
  public menuVisible = false;
  public isLoading = false;
  private isOpening = false;
  private inputPrefixCls = 'apes-input';
  private prefixCls = 'apes-cascader';

  // 内部样式
  private _arrowCls: { [ name: string ]: any };
  private _clearCls: { [ name: string ]: any };
  private _inputCls: { [ name: string ]: any };
  private _labelCls: { [ name: string ]: any };
  private _loadingCls: { [ name: string ]: any };
  private _menuCls: { [ name: string ]: any };
  private _menuColumnCls: { [ name: string ]: any };

  public el: HTMLElement;
  private isFocused = false;

  /** 选择选项后，渲染显示文本 */
  private labelRenderTpl: TemplateRef<any>;
  public labelRenderText: string;
  public labelRenderContext: any = {};

  // 当前值
  private value = {};
  // 已选择的选项表示当前已确认的选项：selection will trigger value change
  private selectedOptions={};
  // 已激活的选项表示通过键盘方向键选择的选项，并未最终确认（除非按ENTER键）：activaction will not trigger value change
  private activatedOptions={};

  // 显示或隐藏菜单计时器
  private delayTimer: any;
  private delaySelectTimer: any;

  /** 搜索相关的输入值 */
  private _inputValue = '';
  get inputValue(): string {
    return this._inputValue;
  }

  set inputValue(inputValue: string) {
    this._inputValue = inputValue;
    this.setClassMap();
  }

  // ngModel Access
  onChange: any = Function.prototype;
  onTouched: any = Function.prototype;

  /** Display Render ngTemplate */
  @Input()
  set apesLabelRender(value: TemplateRef<any>) {
    this.labelRenderTpl = value;
  }

  get apesLabelRender(): TemplateRef<any> {
    return this.labelRenderTpl;
  }

  /** prefixCls */
  @Input()
  set apesPrefixCls(prefixCls: string) {
    this.prefixCls = prefixCls;
    this.setClassMap();
    this.setLabelClass();
    this.setArrowClass();
    this.setLoadingClass();
    this.setClearClass();
    this.setInputClass();
    this.setMenuClass();
  }

  get apesPrefixCls(): string {
    return this.prefixCls;
  }

  @Input()
  set apesReadonly(value:boolean){
    this.readonly = toBoolean(value);
    this.setClassMap();
    this.setInputClass();
  }

  get apesReadonly(): boolean {
    return this.readonly;
  }

  /** Whether is disabled */
  @Input()
  set apesDisabled(value: boolean) {
    this.disabled = toBoolean(value);
    this.setClassMap();
    this.setInputClass();
  }

  get apesDisabled(): boolean {
    return this.disabled;
  }

  /** Input size, one of `large` `default` `small` */
  @Input()
  set apesSize(value: ApesCascaderSize) {
    this.size = value;
    this.setClassMap();
    this.setInputClass();
  }

  get apesSize(): ApesCascaderSize {
    return this.size;
  }

  /** Whether show input box. Defaults to `true`. */
  @Input()
  set apesShowInput(value: boolean) {
    this.showInput = toBoolean(value);
  }

  get apesShowInput(): boolean {
    return this.showInput;
  }

  /** Whether allow clear. Defaults to `true`. */
  @Input()
  set apesAllowClear(value: boolean) {
    this.allowClear = toBoolean(value);
  }

  get apesAllowClear(): boolean {
    return this.allowClear;
  }

  /** Whether auto focus. */
  @Input()
  set apesAutoFocus(value: boolean) {
    this.autoFocus = toBoolean(value);
  }

  get apesAutoFocus(): boolean {
    return this.autoFocus;
  }

  /** Whether to show arrow */
  @Input()
  set apesShowArrow(value: boolean) {
    this.showArrow = toBoolean(value);
  }

  get apesShowArrow(): boolean {
    return this.showArrow;
  }

  /** Additional className of popup overlay */
  @Input()
  set apesMenuClassName(value: string) {
    this.menuClassName = value;
    this.setMenuClass();
  }

  get apesMenuClassName(): string {
    return this.menuClassName;
  }

  @Input()
  set apesColumnClassName(value: string) {
    this.columnClassName = value;
    this.setMenuColumnClass();
  }

  get apesColumnClassName(): string {
    return this.columnClassName;
  }
  /** Change value on each selection if set to true */
  @Input()
  set apesChangeOnSelect(value: boolean) {
    this.changeOnSelect = toBoolean(value);
  }

  get apesChangeOnSelect(): boolean {
    return this.changeOnSelect;
  }

  /** Hover text for the clear icon */
  @Input() apesClearText = 'Clear';

  /** Expand column item when click or hover, one of 'click' 'hover' */
  @Input() apesExpandTrigger: ApesCascaderExpandTrigger = 'click';

  /** Specify content to show when no result matches. */
  @Input() apesNotFoundContent = 'Not Found';

  /** Input placeholder */
  @Input() apesPlaceHolder = 'Please select';

  /** Additional style of popup overlay */
  @Input() apesMenuStyle: { [ key: string ]: string; };

  /** Change value on selection only if this function returns `true` */
  @Input() apesChangeOn: (option: CascaderOption, level: string) => boolean;

  /** Delay time to show when mouse enter, when `apesExpandTrigger` is `hover`. */
  @Input() apesMouseEnterDelay = 150; // ms

  /** Delay time to hide when mouse enter, when `apesExpandTrigger` is `hover`. */
  @Input() apesMouseLeaveDelay = 150; // ms

  /** Triggering mode: can be Array<'click'|'hover'> */
  @Input() apesTriggerAction: ApesCascaderTriggerType | ApesCascaderTriggerType[] = [ 'click' ];
  /** Property name for getting `label` in the option */
  @Input() apesLabelProperty = 'name';
  /** Event: emit on popup show or hide */
  @Output() apesVisibleChange = new EventEmitter<boolean>();
  /** Event: emit on the clear button clicked */
  @Output() apesClear = new EventEmitter<void>();

  public onPositionChange(position: ConnectedOverlayPositionChange): void {
    const newValue = position.connectionPair.originY === 'bottom' ? 'bottom' : 'top';
    if (this.dropDownPosition !== newValue) {
      this.dropDownPosition = newValue;
      this.cdr.detectChanges();
    }
  }

  public focus(): void {
    if (!this.isFocused) {
      const input = this.el.querySelector(`.${this.prefixCls}-input`) as HTMLElement;
      if (input && input.focus) {
        input.focus();
      } else {
        this.el.focus();
      }
      this.isFocused = true;
      this.setClassMap();
    }
  }

  public blur(): void {
    if (this.isFocused) {
      const input = this.el.querySelector(`.${this.prefixCls}-input`) as HTMLElement;
      if (input && input.blur) {
        input.blur();
      } else {
        this.el.blur();
      }
      this.isFocused = false;
      this.setClassMap();
    }
  }

  private setClassMap(): void {
    const classMap = {
      [ `${this.prefixCls}` ]                     : 1,
      [ `${this.prefixCls}-picker` ]              : 1,
      [ `${this.prefixCls}-lg` ]                  : this.apesSize === 'large',
      [ `${this.prefixCls}-sm` ]                  : this.apesSize === 'small',
      [ `${this.prefixCls}-picker-disabled` ]     : this.disabled,
      [ `${this.prefixCls}-picker-readonly` ]     : this.readonly,
      [ `${this.prefixCls}-focused` ]             : this.isFocused,
      [ `${this.prefixCls}-picker-open` ]         : this.menuVisible,
      [ `${this.prefixCls}-picker-with-value` ]   : this.inputValue && this.inputValue.length
    };
    this.updateHostClassService.updateHostClass(this.el, classMap);
  }

  /** 标签 样式 */
  public get labelCls(): any {
    return this._labelCls;
  }

  private setLabelClass(): void {
    this._labelCls = {
      [ `${this.prefixCls}-picker-label` ]: true
    };
  }

  /** 箭头 样式 */
  public get arrowCls(): any {
    return this._arrowCls;
  }

  private setArrowClass(): void {
    this._arrowCls = {
      [ `${this.prefixCls}-picker-arrow` ]       : true,
      [ `${this.prefixCls}-picker-arrow-expand` ]: this.menuVisible
    };
  }

  /** 加载中图标 样式 */
  public get loadingCls(): any {
    return this._loadingCls;
  }

  private setLoadingClass(): void {
    this._loadingCls = {
      [ `${this.prefixCls}-picker-arrow` ]: true
    };
  }

  /** 清除图标 样式 */
  public get clearCls(): any {
    return this._clearCls;
  }

  private setClearClass(): void {
    this._clearCls = {
      [ `${this.prefixCls}-picker-clear` ]: true
    };
  }

  /** 输入框 样式 */
  public get inputCls(): any {
    return this._inputCls;
  }

  private setInputClass(): void {
    this._inputCls = {
      [ `${this.inputPrefixCls}` ]         : true,
      [ `${this.inputPrefixCls}-disabled` ]: this.apesDisabled,
      // [ `${this.inputPrefixCls}-readonly` ]: this.apesReadonly,
      [ `${this.inputPrefixCls}-lg` ]      : this.apesSize === 'large',
      [ `${this.inputPrefixCls}-sm` ]      : this.apesSize === 'small'
    };
  }

  /** 浮层 样式 */
  public get menuCls(): any {
    return this._menuCls;
  }

  private setMenuClass(): void {
    this._menuCls = {
      [ `${this.prefixCls}-menus` ]       : true,
      [ `${this.prefixCls}-menus-hidden` ]: !this.menuVisible,
      [ `${this.apesMenuClassName}` ]       : this.apesMenuClassName
    };
  }
  /** 浮层列 样式 */
  public get menuColumnCls(): any {
    return this._menuColumnCls;
  }

  private setMenuColumnClass(): void {
    this._menuColumnCls = {
      [ `${this.prefixCls}-menu` ]   : true,
      [ `${this.apesColumnClassName}` ]: this.apesColumnClassName
    };
  }
  /** prevent input change event */
  public handlerInputChange(event: Event): void {
    event.stopPropagation();
  }
  /** input element blur */
  public handleInputBlur(event: Event): void {
    if (this.menuVisible) {
      this.focus(); // keep input has focus when menu opened
    } else {
      this.blur();
    }
  }
  /** input element focus */
  public handleInputFocus(event: Event): void {
    this.focus();
  }
  private hasInput(): boolean {
    return this.inputValue.length > 0;
  }

  private hasValue(): boolean {
     return this.value && !missingOrEmptyObject(this.value);
  }

  /** Whether to show input element placeholder */
  public get showPlaceholder(): boolean {
    return !(this.hasInput() || this.hasValue());
  }

  /** Whether the clear button is visible */
  public get showClearIcon(): boolean {
    const isHasValue = this.hasValue();
    const isHasInput = this.hasInput();
    return this.apesAllowClear && !this.apesDisabled && (isHasValue || isHasInput);
  }

  /** clear the input box and selected options */
  public clearSelection(event?: Event): void {
    if (event) {
      event.preventDefault();
      event.stopPropagation();
    }
    this.labelRenderText = '';
    this.selectedOptions = {};
    this.activatedOptions = {};
    this.inputValue = '';
    this.address = {province: '', city: '', district: ''};
    this.setMenuVisible(false);
    // trigger change event
    this.onValueChange();
  }
  private buildDisplayLabel(): void {
    const selectedOptions = this.selectedOptions;
    const labels: string[] = []; //selectedOptions.map(o => this.getOptionLabel(o));
    this.areas.forEach(x => labels.push(this.getOptionLabel(this.selectedOptions[x])));
    // 设置当前控件的显示值
    if (missingOrEmptyObject(this.selectedOptions)) {
      this.labelRenderText = '';
    }else{
      this.labelRenderText = defaultDisplayRender.call(this, labels, selectedOptions);
    }
  }
  get isLabelRenderTemplate(): boolean {
    return !!this.apesLabelRender;
  }
  @HostListener('click', [ '$event' ])
  public onTriggerClick(event: MouseEvent): void {
    if (this.apesDisabled || this.apesReadonly) {
      return;
    }
    this.onTouched(); // set your control to 'touched'
    if (this.isClickTiggerAction()) {
      this.delaySetMenuVisible(!this.menuVisible, 100);
    }
  }

  @HostListener('mouseenter', [ '$event' ])
  public onTriggerMouseEnter(event: MouseEvent): void {
    if (this.apesDisabled || this.apesReadonly) {
      return;
    }
  }

  @HostListener('mouseleave', [ '$event' ])
  public onTriggerMouseLeave(event: MouseEvent): void {
    if (this.apesDisabled || this.apesReadonly) {
      return;
    }
    if (!this.isMenuVisible() || this.isOpening) {
      event.preventDefault();
      return;
    }
  }

  private isClickTiggerAction(): boolean {
    if (typeof this.apesTriggerAction === 'string') {
      return this.apesTriggerAction === 'click';
    }
    return this.apesTriggerAction.indexOf('click') !== -1;
  }

  public closeMenu(): void {
    this.clearDelayTimer();
    this.setMenuVisible(false);
  }

  private clearDelayTimer(): void {
    if (this.delayTimer) {
      clearTimeout(this.delayTimer);
      this.delayTimer = null;
    }
  }

  /**
   * 显示或者隐藏菜单
   *
   * @param visible true-显示，false-隐藏
   * @param delay 延迟时间
   */
  public delaySetMenuVisible(visible: boolean, delay: number, setOpening: boolean = false): void {
    this.clearDelayTimer();
    this.index = 0;
    if (delay) {
      if (visible && setOpening) {
        this.isOpening = true;
      }
      this.delayTimer = setTimeout(() => {
        this.setMenuVisible(visible);
        this.clearDelayTimer();
        if (visible) {
          setTimeout(() => {
            this.isOpening = false;
          }, 100);
        }
      }, delay);
    } else {
      this.setMenuVisible(visible);
    }
  }

  public isMenuVisible(): boolean {
    return this.menuVisible;
  }

  public setMenuVisible(menuVisible: boolean): void {
    if (this.apesDisabled) {
      return;
    }

    if (this.menuVisible !== menuVisible) {
      this.menuVisible = menuVisible;

      // update class
      this.setClassMap();
      this.setArrowClass();
      this.setMenuClass();

      if (menuVisible) {
        this.beforeVisible();
      }
      this.apesVisibleChange.emit(menuVisible);
    }
  }

  /** load init data if necessary */
  private beforeVisible(): void {
    this.loadRootOptions();
  }

  private loadRootOptions(): void {
    if ( !this.province_key.length){
      this.areaService.getProvince().subscribe(
        (data) =>{
           data.forEach((x) =>{
            if (this.provinces[x.group] == undefined) {
              this.provinces[x.group] = [];
            }
            this.provinces[x.group].push(x);
          });
          this.province_key = ['A-G','H-K','L-S','T-Z'];
        }
      )
    }
  }

  /** 获取Option的值，例如，可以指定labelProperty="name"来取Name */
  public getOptionLabel(option: CascaderOption): any {
    return exists(option) ? option['name']: ''
  }

  /** 获取Option的值，例如，可以指定valueProperty="id"来取ID */
  public getOptionValue(option: CascaderOption): any {
    return option['id' ];
  }


  /**
   * 设置某列的激活的菜单选项
   *
   * @param option 菜单选项
   * @param index  选项所在的列组的索引
   * @param select 是否触发选择结点
   */
  private setActiveOption(option: CascaderOption, type: string, select: boolean = false, loadChildren: boolean = true): void {
    if (!option) {
      return;
    }
    this.activatedOptions[type] = option;
    // load children
    if (type === this.PROVINCE) {
      this.address.province = option.id;
      this.address.city = '';
      this.index = this.index + 1;
      this.loadChildren(this.citys,option);
    } else if (type === this.CITY) {
      this.address.city = option.id;
      this.loadChildren(this.districts,option);
      this.index = this.index + 1;
    } else if (type === this.DISTRICT) {
      this.address.district = option.id;
    }

    // trigger select event, and display label
    if (select) {
      this.onSelectOption(option, type);
    }
  }

  private loadChildren(type,option): void {
    let key = option.id;
    if (type[key] === undefined) {
      this.areaService.getChildren(option).subscribe(item =>
        type[key] = item
      )
    }
  }

  private onSelectOption(option: CascaderOption, type: string): void {
    // trigger `apesSelect` event
    // this.apesSelect.emit({ option, type });
    // 生成显示
    if (type === this.level || this.apesChangeOnSelect || this.isChangeOn(option, type)) {
      this.selectedOptions = this.activatedOptions;
      // 设置当前控件的显示值
      this.buildDisplayLabel();
      // 触发变更事件
      this.onValueChange();
    }

    // close menu if click on leaf
    if (type === this.level) {
      this.delaySetMenuVisible(false, this.apesMouseLeaveDelay);
    }
  }
  /** 由用户来定义点击后是否变更 */
  private isChangeOn(option: CascaderOption, type: string): boolean {
    if (typeof this.apesChangeOn === 'function') {
      return this.apesChangeOn(option, type) === true;
    }
    return false;
  }


  /**
   * 鼠标点击选项
   *
   * @param option 菜单选项
   * @param index 选项所在的列组的索引
   * @param event 鼠标事件
   */
  onOptionClick(type: string,option: CascaderOption,event: Event): void {
    //event.preventDefault();
    // Keep focused state for keyboard support
    this.el.focus();
    this.setActiveOption(option, type, true);
  }

  PROVINCE = 'province';
  CITY = 'city';
  DISTRICT = 'district';
  @Input()
  level: string = 'district';
  @Input()
  address = {province: '', city: '', district: ''};
  @Output()
  closeTest = new EventEmitter();

  index  = 0;
  province_key = [];
  provinces = {};

  citys = {};
  districts = {};
  areas = [];

  defineAreas () {
    let stop = false;
    [this.PROVINCE, this.CITY, this.DISTRICT].forEach(type => {
      if (!stop) {
        this.areas.push(type);
      }
      if (type === this.level) {
        stop = true;
      }
    });
  }
  includeArea(type) {
    return this.areas.indexOf(type) > -1;
  }

  selectChange(param) {
    this.index = param.index;
 }

  private clearDelaySelectTimer(): void {
    if (this.delaySelectTimer) {
      clearTimeout(this.delaySelectTimer);
      this.delaySelectTimer = null;
    }
  }

  public getSubmitValue(): any {
    return this.selectedOptions[this.level];
  }

  private onValueChange(): void {
     const value = this.getSubmitValue();
     if (!deepCompare(this.value, value)) {
      this.defaultValue = null; // clear the init-value
      this.value = value;
      this.onChange(value); // Angular need this
      if (missingOrEmptyObject(value)) {
        this.apesClear.emit(); // first trigger `clear` and then `change`
      }
      //this.apesSelectionChange.emit(this.selectedOptions);
      //this.apesChange.emit(value);
   }
  }

  constructor(private elementRef: ElementRef,
              private cdr: ChangeDetectorRef,
              private updateHostClassService: ApesUpdateHostClassService,
              private areaService:AreaService,
              @Host() @Optional() public noAnimation?: ApesNoAnimationDirective) {
    this.el = this.elementRef.nativeElement;

  }

  private initOptions(type: string): void {
    const vs = this.defaultValue;


    this.areaService.getOption(vs).subscribe((item)=>{
          this.activatedOptions[this.level]=item;
          let options ={};
          options['id']=item['parent'];
          if (this.level === this.DISTRICT){
            this.areaService.getOption(options).pipe(
              map((option) => {
                this.activatedOptions[this.CITY]=option;
                return {id:option.parent}
              }),
              mergeMap(option =>  this.areaService.getOption(option))
            ).subscribe((data) => {
              this.activatedOptions[this.PROVINCE] = data;
              this.afterWriteValue();
            });
          }else if(this.level === this.CITY){
            this.areaService.getOption(options).subscribe( data =>{
                this.activatedOptions[this.PROVINCE] = data;
                this.afterWriteValue()
            });
          }else {
            this.activatedOptions[this.PROVINCE] = item;
            this.afterWriteValue();
          }
      }
    );



 }

  afterWriteValue(): void {
    this.selectedOptions = this.activatedOptions;
    this.value = this.getSubmitValue();
    this.buildDisplayLabel();
  }
  /**
   * Write a new value to the element.
   *
   * @Override (From ControlValueAccessor interface)
   */
  writeValue(value: any): void {
    const vs = this.defaultValue =  value;
     if (! missingOrEmptyObject(vs)) {
         this.initOptions(vs);
     } else {
      this.value = vs;
      this.activatedOptions = {};
      this.afterWriteValue();
    }
  }

  registerOnChange(fn: (_: any) => {}): void {
     this.onChange = fn;
  }

  registerOnTouched(fn: () => {}): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    if (isDisabled) {
      this.closeMenu();
    }
    this.apesDisabled = isDisabled;
  }

  ngOnInit(): void {
    // 设置样式
    this.setClassMap();
    this.setLabelClass();
    this.setArrowClass();
    this.setLoadingClass();
    this.setClearClass();
    this.setInputClass();
    //this.setMenuClass();
    //this.setMenuColumnClass();pi(filter(x => missingOrEmpty(x.parent));
    this.defineAreas();
    // this.areaService.getAreas().subscribe(items =>{
    //     this.provinceArray = items.filter(x => missing(x.parent));
    // });

  }

  ngOnDestroy(): void {
    this.clearDelayTimer();
    this.clearDelaySelectTimer();
  }

}

